<!DOCTYPE html>



  


<html class="theme-next muse use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />















  
  
  <link href="/blog/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />




  
  
  
  

  
    
    
  

  

  

  

  

  
    
    
    <link href="//fonts.googleapis.com/css?family=Lato:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
  






<link href="/blog/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/blog/css/main.css?v=5.1.2" rel="stylesheet" type="text/css" />


  <meta name="keywords" content="Hexo, NexT" />








  <link rel="shortcut icon" type="image/x-icon" href="/blog/favicon.ico?v=5.1.2" />






<meta name="description" content="1. WebSocket 简介1.1. 什么是 WebSocketWebSocket 是 HTML5 开始提供的一种浏览器与服务器间进行全双工通讯的网络技术，可以传输基于信息的文本和二进制的数据。它于 2011 年被 IETF 定为标准 RFC 6455，同时 WebSocket API 也被 W3C 定为标准。 WebSocket 在连接之后，客户端可以主动发送消息给服务器，服务器也可以主动向客">
<meta property="og:type" content="article">
<meta property="og:title" content="HTML5 之 WebSocket™">
<meta property="og:url" content="http://xiezixi.oschina.io/2018/06/01/HTML5-WebSocket/index.html">
<meta property="og:site_name" content="个人博客">
<meta property="og:description" content="1. WebSocket 简介1.1. 什么是 WebSocketWebSocket 是 HTML5 开始提供的一种浏览器与服务器间进行全双工通讯的网络技术，可以传输基于信息的文本和二进制的数据。它于 2011 年被 IETF 定为标准 RFC 6455，同时 WebSocket API 也被 W3C 定为标准。 WebSocket 在连接之后，客户端可以主动发送消息给服务器，服务器也可以主动向客">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="http://xiezixi.oschina.io/blog/images/HTTP与WebSocket连接生命周期.png">
<meta property="og:image" content="http://xiezixi.oschina.io/blog/images/webSocket1.png">
<meta property="og:image" content="http://xiezixi.oschina.io/blog/images/webSocket2.png">
<meta property="og:updated_time" content="2018-06-01T08:42:17.417Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="HTML5 之 WebSocket™">
<meta name="twitter:description" content="1. WebSocket 简介1.1. 什么是 WebSocketWebSocket 是 HTML5 开始提供的一种浏览器与服务器间进行全双工通讯的网络技术，可以传输基于信息的文本和二进制的数据。它于 2011 年被 IETF 定为标准 RFC 6455，同时 WebSocket API 也被 W3C 定为标准。 WebSocket 在连接之后，客户端可以主动发送消息给服务器，服务器也可以主动向客">
<meta name="twitter:image" content="http://xiezixi.oschina.io/blog/images/HTTP与WebSocket连接生命周期.png">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/blog/',
    scheme: 'Muse',
    sidebar: {"position":"left","display":"post","offset":12,"offset_float":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: true,
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="http://xiezixi.oschina.io/2018/06/01/HTML5-WebSocket/"/>





  <title>HTML5 之 WebSocket™ | 个人博客</title>
  














</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">
  
  
    
  

  <div class="container sidebar-position-left page-post-detail ">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/blog/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">个人博客</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle">曦瓜皮</p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/blog/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/blog/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br />
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/blog/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            归档
          </a>
        </li>
      

      
    </ul>
  

  
</nav>


 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://xiezixi.oschina.io/blog/2018/06/01/HTML5-WebSocket/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="曦瓜皮">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="个人博客">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">HTML5 之 WebSocket™</h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-06-01T16:42:17+08:00">
                2018-06-01
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/HTML5/" itemprop="url" rel="index">
                    <span itemprop="name">HTML5</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <h2 id="1-WebSocket-简介"><a href="#1-WebSocket-简介" class="headerlink" title="1. WebSocket 简介"></a>1. WebSocket 简介</h2><h3 id="1-1-什么是-WebSocket"><a href="#1-1-什么是-WebSocket" class="headerlink" title="1.1. 什么是 WebSocket"></a>1.1. 什么是 WebSocket</h3><p>WebSocket 是 HTML5 开始提供的一种浏览器与服务器间进行全双工通讯的网络技术，可以传输基于信息的文本和二进制的数据。它于 2011 年被 IETF 定为标准 RFC 6455，同时 WebSocket API 也被 W3C 定为标准。</p>
<p>WebSocket 在连接之后，客户端可以主动发送消息给服务器，服务器也可以主动向客户端推送消息。比如：预订车票信息，除了我们发请求询问车票如何，当然更希望如果有新消息，可以直接通知我们；以及通常见的 web 端实时通讯，实时性的小游戏等等。</p>
<p>其特点：</p>
<ol>
<li>握手阶段采用 HTTP 协议，默认端口是80和443;</li>
<li>建立在 TCP 协议基础之上，和 HTTP 协议同属于应用层;</li>
<li>可以发送文本，也可以发送二进制数据;</li>
<li>没有同源限制，客户端可以与任意服务器通信;</li>
<li>协议标识符是 ws （如果加密，为 wss），如 <code>ws://localhost:8080</code>;</li>
</ol>
<p><img src="http://xiezixi.oschina.io/blog/images/HTTP与WebSocket连接生命周期.png" alt="HTTP 与 WebSocket 连接生命周期"></p>
<h3 id="1-2-为什么用-WebSocket"><a href="#1-2-为什么用-WebSocket" class="headerlink" title="1.2. 为什么用 WebSocket"></a>1.2. 为什么用 WebSocket</h3><p>HTTP 协议有一个缺陷：服务器不能主动联系客户端，只能由客户端发起。它的被动性预示了在完成双向通信时需要不停的连接或连接一直打开，这就需要服务器快速的处理速度或高并发的能力，是非常消耗资源的。</p>
<p>而 WebSocket 的最大特点就是，服务器可以主动向客户端推送信息，客户端也可以主动向服务器发送信息，是真正的双向平等对话，属于服务器推送技术的一种。</p>
<h3 id="1-3-WebSocket-协议的规范"><a href="#1-3-WebSocket-协议的规范" class="headerlink" title="1.3. WebSocket 协议的规范"></a>1.3. WebSocket 协议的规范</h3><p>以下是一个典型的 WebSocket 发起请求到响应请求的示例：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">客户端到服务端：</span><br><span class="line">GET / HTTP/1.1</span><br><span class="line">Connection:Upgrade</span><br><span class="line">Host:127.0.0.1:8088</span><br><span class="line">Origin:null</span><br><span class="line">Sec-WebSocket-Extensions:x-webkit-deflate-frame</span><br><span class="line">Sec-WebSocket-Key:puVOuWb7rel6z2AVZBKnfw==</span><br><span class="line">Sec-WebSocket-Version:13</span><br><span class="line">Upgrade:websocket</span><br><span class="line"></span><br><span class="line">服务端到客户端：</span><br><span class="line">HTTP/1.1 101 Switching Protocols</span><br><span class="line">Connection:Upgrade</span><br><span class="line">Server:beetle websocket server</span><br><span class="line">Upgrade:WebSocket</span><br><span class="line">date: Thu, 10 May 2018 07:32:25 GMT</span><br><span class="line">Access-Control-Allow-Credentials:true</span><br><span class="line">Access-Control-Allow-Headers:content-type</span><br><span class="line">Sec-WebSocket-Accept:FCKgUr8c7OsDsLFeJTWrJw6WO8Q=</span><br></pre></td></tr></table></figure>
<p>我们可以看到，WebSocket 协议和 HTTP 协议乍看并没有太大的区别，但细看下来，区别还是有些的，这其实是一个握手的http请求，首先请求和响应的，<code>Upgrade:WebSocket</code>表示请求的目的就是要将客户端和服务器端的通讯协议从 HTTP 协议升级到 WebSocket协议。从客户端到服务器端请求的信息里包含有<code>Sec-WebSocket-Extensions</code>、<code>Sec-WebSocket-Key</code>这样的头信息。这是客户端浏览器需要向服务器端提供的握手信息，服务器端解析这些头信息，并在握手的过程中依据这些信息生成一个28位的安全密钥并返回给客户端，以表明服务器端获取了客户端的请求，同意创建 WebSocket 连接。</p>
<p>当握手成功后，这个时候 TCP 连接就已经建立了，客户端与服务端就能够直接通过 WebSocket 直接进行数据传递。不过服务端还需要判断一次数据请求是什么时候开始的和什么时候是请求的结束的。在 WebSocket 中，由于浏览端和服务端已经打好招呼，如我发送的内容为 utf-8  编码，如果我发送 0x00,表示包的开始，如果发送了 0xFF，就表示包的结束了。这就解决了黏包的问题。</p>
<h3 id="1-4-浏览器兼容性"><a href="#1-4-浏览器兼容性" class="headerlink" title="1.4. 浏览器兼容性"></a>1.4. 浏览器兼容性</h3><p><a href="https://www.caniuse.com/#search=websocket" target="_blank" rel="noopener">WebSocket 浏览器兼容性</a></p>
<h2 id="2-HTML5-WebSocket-API"><a href="#2-HTML5-WebSocket-API" class="headerlink" title="2. HTML5 WebSocket API"></a>2. HTML5 WebSocket API</h2><p>WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。在 WebSocket API 中，浏览器和服务器只需要做一个握手的动作，然后，浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。</p>
<p>浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求，连接建立以后，客户端和服务器端就可以通过 TCP 连接直接交换数据。当你获取 WebSocket 连接后，你可以通过 <code>send()</code> 方法来向服务器发送数据，并通过 <code>onmessage</code> 事件来接收服务器返回的数据。</p>
<h3 id="2-1-WebSocket-对象"><a href="#2-1-WebSocket-对象" class="headerlink" title="2.1. WebSocket 对象"></a>2.1. WebSocket 对象</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> webSocket = <span class="keyword">new</span> WebSocket(url, [protocol] );</span><br></pre></td></tr></table></figure>
<p>以上代码中的第一个参数 url, 指定连接的 URL。第二个参数 protocol 是可选的，指定了可接受的子协议。</p>
<h3 id="2-2-WebSocket属性"><a href="#2-2-WebSocket属性" class="headerlink" title="2.2. WebSocket属性"></a>2.2. WebSocket属性</h3><p>以下是 WebSocket 对象的属性。假定我们使用了以上代码创建了 webSocket 对象：</p>
<table>
<thead>
<tr>
<th>属性</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>webSocket.readyState</td>
<td>连接状态</td>
</tr>
<tr>
<td>webSocket.bufferedAmount</td>
<td>只读属性 <code>bufferedAmount</code> 已被 <code>send()</code> 放入正在队列中等待传输，但是还没有发出的 UTF-8 文本字节数</td>
</tr>
</tbody>
</table>
<p>webSocket.readyState：</p>
<ul>
<li>0 表示连接尚未建立</li>
<li>1 表示连接已建立，可以进行通信</li>
<li>2 表示连接正在进行关闭</li>
<li>3 表示连接已经关闭或者连接不能打开</li>
</ul>
<h3 id="2-3-WebSocket-事件"><a href="#2-3-WebSocket-事件" class="headerlink" title="2.3. WebSocket 事件"></a>2.3. WebSocket 事件</h3><p>以下是 WebSocket 对象的相关事件。假定我们使用了以上代码创建了 webSocket 对象：</p>
<table>
<thead>
<tr>
<th>事件</th>
<th>事件处理程序</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>open</td>
<td>webSocket.onopen</td>
<td>连接建立时触发</td>
</tr>
<tr>
<td>message</td>
<td>webSocket.message</td>
<td>客户端接收服务端数据时触发</td>
</tr>
<tr>
<td>error</td>
<td>webSocket.onerror</td>
<td>通信发生错误时触发</td>
</tr>
<tr>
<td>close</td>
<td>webSocket.onclose</td>
<td>连接关闭时触发</td>
</tr>
</tbody>
</table>
<h3 id="2-4-WebSocket-方法"><a href="#2-4-WebSocket-方法" class="headerlink" title="2.4. WebSocket 方法"></a>2.4. WebSocket 方法</h3><p>以下是 WebSocket 对象的相关方法。假定我们使用了以上代码创建了 webSocket 对象：</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>webSocket.send()</td>
<td>使用连接发送数据</td>
</tr>
<tr>
<td>webSocket.close()</td>
<td>关闭连接</td>
</tr>
</tbody>
</table>
<h2 id="3-客户端实现"><a href="#3-客户端实现" class="headerlink" title="3. 客户端实现"></a>3. 客户端实现</h2><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE html&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"X-UA-Compatible"</span> <span class="attr">content</span>=<span class="string">"ie=edge"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>WebSocket 简单实例<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="undefined"></span></span><br><span class="line"><span class="javascript">    <span class="comment">// 创建websocket对象，与websocket官网建立websocket连接</span></span></span><br><span class="line"><span class="javascript">    <span class="keyword">var</span> ws = <span class="keyword">new</span> WebSocket(<span class="string">"wss://echo.websocket.org"</span>);</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript">    <span class="comment">// 建立连接时触发</span></span></span><br><span class="line"><span class="javascript">    ws.onopen = <span class="function"><span class="keyword">function</span> (<span class="params">evt</span>) </span>&#123;</span></span><br><span class="line"><span class="javascript">        <span class="built_in">console</span>.log(<span class="string">"Connection open ..."</span>);</span></span><br><span class="line"><span class="javascript">        <span class="comment">// 向服务端发送数据</span></span></span><br><span class="line"><span class="javascript">        ws.send(<span class="string">"Hello WebSockets!"</span>);</span></span><br><span class="line"><span class="undefined">    &#125;;</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript">    <span class="comment">// 客户端接收服务端数据时触发</span></span></span><br><span class="line"><span class="javascript">    ws.onmessage = <span class="function"><span class="keyword">function</span> (<span class="params">evt</span>) </span>&#123;</span></span><br><span class="line"><span class="javascript">        <span class="built_in">console</span>.log(<span class="string">"Received Message: "</span> + evt.data);</span></span><br><span class="line"><span class="javascript">        <span class="comment">// 关闭连接</span></span></span><br><span class="line"><span class="undefined">        ws.close();</span></span><br><span class="line"><span class="undefined">    &#125;;</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript">    <span class="comment">// 连接关闭时触发</span></span></span><br><span class="line"><span class="javascript">    ws.onclose = <span class="function"><span class="keyword">function</span> (<span class="params">evt</span>) </span>&#123;</span></span><br><span class="line"><span class="javascript">        <span class="built_in">console</span>.log(<span class="string">"Connection closed."</span>);</span></span><br><span class="line"><span class="undefined">    &#125;;</span></span><br><span class="line"><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p><img src="http://xiezixi.oschina.io/blog/images/webSocket1.png" alt="WebSocket 数据交互信息"></p>
<p><img src="http://xiezixi.oschina.io/blog/images/webSocket2.png" alt="WebSocket 具体连接信息"></p>
<h2 id="4-服务端实现"><a href="#4-服务端实现" class="headerlink" title="4. 服务端实现"></a>4. 服务端实现</h2><p>当然，使用 WebSocket 需要服务端与客户端都提供相应能力，这里基于 Node.js 和 ws 简单建立一个服务端 WebSocket 接口：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>);</span><br><span class="line"><span class="keyword">const</span> WebSocket = <span class="built_in">require</span>(<span class="string">'ws'</span>);</span><br><span class="line"><span class="keyword">const</span> http = <span class="built_in">require</span>(<span class="string">'http'</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> app = express();</span><br><span class="line"></span><br><span class="line">app.get(<span class="string">'/'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">req, res</span>) </span>&#123;</span><br><span class="line">    res.sendfile(<span class="string">'./index.html'</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> server = http.createServer(app);</span><br><span class="line"><span class="keyword">const</span> wss = <span class="keyword">new</span> WebSocket.Server(&#123; server &#125;);</span><br><span class="line"></span><br><span class="line">wss.on(<span class="string">'connection'</span>, <span class="function"><span class="keyword">function</span> <span class="title">connection</span>(<span class="params">ws, req</span>) </span>&#123;</span><br><span class="line">    ws.on(<span class="string">'message'</span>, <span class="function"><span class="keyword">function</span> <span class="title">incoming</span>(<span class="params">message</span>) </span>&#123;</span><br><span class="line">        ws.send(<span class="string">'received: '</span> + message + <span class="string">'(From Server)'</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">    ws.send(<span class="string">'Hello Client'</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">server.listen(<span class="number">8080</span>, <span class="function"><span class="keyword">function</span> <span class="title">listening</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'Listening on %d'</span>, server.address().port);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h2 id="5-事件与数据"><a href="#5-事件与数据" class="headerlink" title="5. 事件与数据"></a>5. 事件与数据</h2><p>对 WebSocket 实例监听事件有两种方式，这里以 message 事件为例：</p>
<ol>
<li>对 <code>onmessage</code> 属性直接赋值，正如以上：<code>ws.onmessage = function () {};</code>；</li>
<li>使用 <code>addEventListener</code> 监听事件，如：<code>ws.addEventListener(&#39;message&#39;, function () {})</code>；</li>
</ol>
<p>在 message 回调函数中得到 MessageEvent 类型参数 e ，我们需要的数据可以通过 e.data 获取；<br>需要注意的一点是：不论服务端与客户端，其接受到的数据都是序列化后的字符串（当然也有 ArrayBuffer | Blob 类型数据），很多时候我们需要解析处理数据，比如 <code>JSON.parse(e.data)</code>；</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">webSocket.onmessage = <span class="function">(<span class="params">evt</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// 接受消息</span></span><br><span class="line">  <span class="keyword">let</span> data = &#123;&#125;;</span><br><span class="line">    data = <span class="built_in">JSON</span>.parse(evt.data);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="6-心跳重连"><a href="#6-心跳重连" class="headerlink" title="6. 心跳重连"></a>6. 心跳重连</h2><p>由于网络环境复杂，某些情况会出现断开连接或者连接出错，需要我们在 close 或者 error 事件中监听非正常断开并重连；由于一些原因在 error 时浏览器并不会响应回调事件，因此稳妥的做法还需要在 open 之后开启一个定时任务去判断当前的连接状态 <code>readyState</code> ，在出现异常情况下尝试重连。</p>
<p>webSocket 规范定义了心跳机制，一方可以通过发送 ping（opcode 0x9）消息给另一方，另一方收到 ping 后应该尽可能快的返回 pong（0xA）。</p>
<p>心跳机制是用于检测连接的对方在线状态，因此如果没有心跳，那么无法判断一方还在连接状态中，一些网络层比如 nginx 或者浏览器层会主动断开连接，</p>
<p>在 JavaScript 中，WebSocket 并没有开放 ping/pong 的 API ，虽然浏览器自带了心跳处理，然而不同厂商的实现也不尽相同，因此需要在我们开发时候与服务端约定好一个自实现的心跳机制；</p>
<p>比如浏览器中，检测到 open 事件后，启动一个定时任务，每次发送数据 <code>0x9</code> 给服务端，而服务端返回 <code>0xA</code> 作为响应；</p>
<p>实践下来，心跳的定时任务一般是相隔 15-20 秒发送一次。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// webSocket心跳重连</span></span><br><span class="line"><span class="keyword">const</span> heartCheck = &#123;</span><br><span class="line">  interval: <span class="number">45000</span>, <span class="comment">// 45ms</span></span><br><span class="line">  intervalObj: <span class="literal">null</span>,</span><br><span class="line">  reset() &#123;</span><br><span class="line">    clearInterval(<span class="keyword">this</span>.intervalObj);</span><br><span class="line">    <span class="keyword">this</span>.start();</span><br><span class="line">  &#125;,</span><br><span class="line">  start() &#123;</span><br><span class="line">    <span class="keyword">this</span>.intervalObj = setInterval(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">      webSocket.send(<span class="string">'0x9'</span>);</span><br><span class="line">    &#125;, <span class="keyword">this</span>.interval);</span><br><span class="line">  &#125;,</span><br><span class="line">  remove() &#123;</span><br><span class="line">    clearInterval(<span class="keyword">this</span>.intervalObj);</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// webSocket连接时回调</span></span><br><span class="line">webSocket.onopen = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  heartCheck.start();</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// webSocket断开时回调</span></span><br><span class="line">webSocket.onclose = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  heartCheck.remove();</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// webSocket发生错误时回调</span></span><br><span class="line">webSocket.onerror = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  heartCheck.remove();</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h2 id="7-WSS"><a href="#7-WSS" class="headerlink" title="7. WSS"></a>7. WSS</h2><p>创建一个 socket 实例时可以选填 ws 和 wss 来进行通信协议的确定。他们两个其实很像 HTTP 和 HTTPS 之间的关系。其中ws表示纯文本通信，而 wss 表示使用加密信道通信（TCP + TLS）。那为啥不直接使用 HTTP 而要自定义通信协议呢？这就要从WebSocket的目的说起来，WebSocket 的主要功能就是为了给浏览器中的应用与服务器端提供优化的，双向的通信机制，但这不代表 WebSocket 只能局限于此，它当然还能够用于其他的场景，这就需要他可以通过非 HTTP 协议来进行数据交换，因此 WebSocket 也就采用了自定义 URI 模式，以确保就算没有 HTTP，也能进行数据交换。</p>
<p>在 HTTP 协议中，很多时候为了加密与安全需要使用 HTTPS 请求（HTTP + TCL）；<br>相应的，在 WebSocket 协议中，也是可以使用加密传输的 —— wss ，比如 <code>wss://localhost:8080</code>。</p>
<p>使用的也是与 HTTPS 一样的证书，在这里一般是交由 Nginx 等服务层去做证书处理。</p>
<h2 id="8-优秀开源框架"><a href="#8-优秀开源框架" class="headerlink" title="8. 优秀开源框架"></a>8. 优秀开源框架</h2><p><a href="https://github.com/socketio/socket.io" target="_blank" rel="noopener">socket.io</a></p>

      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/blog/2018/05/16/微信登录/" rel="next" title="微信扫码登录">
                <i class="fa fa-chevron-left"></i> 微信扫码登录
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/blog/2018/06/01/HTML5-WebStorage/" rel="prev" title="HTML5 之 WebStorage™">
                HTML5 之 WebStorage™ <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          
  <div class="comments" id="comments">
    
  </div>


        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap" >
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
          <img class="site-author-image" itemprop="image"
               src="/blog/images/avatar.jpg"
               alt="曦瓜皮" />
          <p class="site-author-name" itemprop="name">曦瓜皮</p>
           
              <p class="site-description motion-element" itemprop="description"></p>
          
        </div>
        <nav class="site-state motion-element">

          
            <div class="site-state-item site-state-posts">
              <a href="/blog/archives/">
                <span class="site-state-item-count">15</span>
                <span class="site-state-item-name">日志</span>
              </a>
            </div>
          

          
            
            
            <div class="site-state-item site-state-categories">
              <a href="/blog/categories/index.html">
                <span class="site-state-item-count">6</span>
                <span class="site-state-item-name">分类</span>
              </a>
            </div>
          

          

        </nav>

        

        <div class="links-of-author motion-element">
          
        </div>

        
        

        
        

        

      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-WebSocket-简介"><span class="nav-number">1.</span> <span class="nav-text">1. WebSocket 简介</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-1-什么是-WebSocket"><span class="nav-number">1.1.</span> <span class="nav-text">1.1. 什么是 WebSocket</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-2-为什么用-WebSocket"><span class="nav-number">1.2.</span> <span class="nav-text">1.2. 为什么用 WebSocket</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-3-WebSocket-协议的规范"><span class="nav-number">1.3.</span> <span class="nav-text">1.3. WebSocket 协议的规范</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-4-浏览器兼容性"><span class="nav-number">1.4.</span> <span class="nav-text">1.4. 浏览器兼容性</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-HTML5-WebSocket-API"><span class="nav-number">2.</span> <span class="nav-text">2. HTML5 WebSocket API</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#2-1-WebSocket-对象"><span class="nav-number">2.1.</span> <span class="nav-text">2.1. WebSocket 对象</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-2-WebSocket属性"><span class="nav-number">2.2.</span> <span class="nav-text">2.2. WebSocket属性</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-3-WebSocket-事件"><span class="nav-number">2.3.</span> <span class="nav-text">2.3. WebSocket 事件</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-4-WebSocket-方法"><span class="nav-number">2.4.</span> <span class="nav-text">2.4. WebSocket 方法</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-客户端实现"><span class="nav-number">3.</span> <span class="nav-text">3. 客户端实现</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-服务端实现"><span class="nav-number">4.</span> <span class="nav-text">4. 服务端实现</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-事件与数据"><span class="nav-number">5.</span> <span class="nav-text">5. 事件与数据</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#6-心跳重连"><span class="nav-number">6.</span> <span class="nav-text">6. 心跳重连</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#7-WSS"><span class="nav-number">7.</span> <span class="nav-text">7. WSS</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#8-优秀开源框架"><span class="nav-number">8.</span> <span class="nav-text">8. 优秀开源框架</span></a></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright" >
  
  &copy; 
  <span itemprop="copyrightYear">2018</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">曦瓜皮</span>
</div>


<div class="powered-by">
  由 <a class="theme-link" href="https://hexo.io">Hexo</a> 强力驱动
</div>

<div class="theme-info">
  主题 -
  <a class="theme-link" href="https://github.com/iissnan/hexo-theme-next">
    NexT.Muse
  </a>
</div>


        

        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  <script type="text/javascript" src="/blog/lib/jquery/index.js?v=2.1.3"></script>

  
  <script type="text/javascript" src="/blog/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>

  
  <script type="text/javascript" src="/blog/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>

  
  <script type="text/javascript" src="/blog/lib/velocity/velocity.min.js?v=1.2.1"></script>

  
  <script type="text/javascript" src="/blog/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>

  
  <script type="text/javascript" src="/blog/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>


  


  <script type="text/javascript" src="/blog/js/src/utils.js?v=5.1.2"></script>

  <script type="text/javascript" src="/blog/js/src/motion.js?v=5.1.2"></script>



  
  

  
  <script type="text/javascript" src="/blog/js/src/scrollspy.js?v=5.1.2"></script>
<script type="text/javascript" src="/blog/js/src/post-details.js?v=5.1.2"></script>



  


  <script type="text/javascript" src="/blog/js/src/bootstrap.js?v=5.1.2"></script>



  


  




	





  





  






  





  

  

  

  

  

  

</body>
</html>
